#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Zabbix API客户端模块 - 处理与Zabbix API的所有交互
"""

import logging
import requests
import json
from typing import Dict, List, Any, Optional, Union
from urllib3.exceptions import InsecureRequestWarning

# 禁用不安全的HTTPS警告
# 移除这行错误代码
# requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# 正确的导入方式
import urllib3
urllib3.disable_warnings(InsecureRequestWarning)

logger = logging.getLogger(__name__)

class ZabbixAPIException(Exception):
    """Zabbix API异常"""
    pass

class ZabbixAPI:
    """Zabbix API客户端类"""
    
    def __init__(self, url: str, user: str, password: str, timeout: int = 30, verify: bool = True):
        """
        初始化Zabbix API客户端
        
        Args:
            url (str): Zabbix API URL
            user (str): 用户名
            password (str): 密码
            timeout (int, optional): 超时时间（秒）
            verify (bool, optional): 是否验证SSL证书
        """
        self.url = url
        self.user = user
        self.password = password
        self.timeout = timeout
        self.verify = verify
        self.auth = None
        self.id = 1  # API请求ID
        
        # 自动登录
        if user and password:
            self.login()
    
    def login(self) -> bool:
        """
        登录Zabbix API
        
        Returns:
            bool: 是否登录成功
        """
        try:
            result = self._call("user.login", {
                "user": self.user,
                "password": self.password
            })
            self.auth = result
            logger.info(f"成功登录Zabbix API: {self.url}")
            return True
        except Exception as e:
            logger.error(f"登录Zabbix API失败: {e}")
            return False
    
    def _preprocess_api_params(self, method: str, params: Union[Dict[str, Any], List[Dict[str, Any]]]) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
        """
        预处理API参数，确保参数格式正确
        
        Args:
            method (str): API方法名称
            params (Union[Dict[str, Any], List[Dict[str, Any]]]): API参数
            
        Returns:
            Union[Dict[str, Any], List[Dict[str, Any]]]: 处理后的参数
        """
        # 深拷贝参数，避免修改原始数据
        import copy
        processed_params = copy.deepcopy(params)
        
        # 处理item.create方法的参数
        if method == "item.create":
            # 处理情况1: 参数是字典，并且包含items键
            if isinstance(processed_params, dict) and "items" in processed_params:
                items = processed_params["items"]
                if isinstance(items, list):
                    for i, item in enumerate(items):
                        if not isinstance(item, dict):
                            logger.warning(f"第{i+1}个监控项不是字典类型，跳过处理")
                            continue
                        
                        # 确保type参数存在且为整数(注意: Zabbix API要求type为整数)
                        if "type" not in item or item["type"] is None:
                            logger.warning(f"预处理：第{i+1}个监控项缺少type参数，设置为默认值0")
                            items[i]["type"] = 0
                        elif isinstance(item["type"], str):
                            # 如果是字符串，转换为整数
                            try:
                                items[i]["type"] = int(item["type"])
                                logger.warning(f"预处理：第{i+1}个监控项的type参数从字符串转换为整数")
                            except ValueError:
                                logger.warning(f"预处理：第{i+1}个监控项的type参数'{item['type']}'无法转换为整数，设置为默认值0")
                                items[i]["type"] = 0
                
            # 处理情况2: 参数是单个监控项字典(不包含items键)
            elif isinstance(processed_params, dict) and "name" in processed_params and "key_" in processed_params:
                # 这是单个监控项对象的典型特征(有name和key_字段)
                logger.debug("检测到单个监控项对象")
                
                # 确保type参数存在且为整数
                if "type" not in processed_params or processed_params["type"] is None:
                    logger.warning(f"预处理：单个监控项缺少type参数，设置为默认值0")
                    processed_params["type"] = 0
                elif isinstance(processed_params["type"], str):
                    # 如果是字符串，转换为整数
                    try:
                        processed_params["type"] = int(processed_params["type"])
                        logger.warning(f"预处理：单个监控项的type参数从字符串转换为整数")
                    except ValueError:
                        logger.warning(f"预处理：单个监控项的type参数'{processed_params['type']}'无法转换为整数，设置为默认值0")
                        processed_params["type"] = 0
                
                # 记录处理后的关键参数
                logger.debug(f"预处理后，单个监控项: name={processed_params.get('name', 'N/A')}, type={processed_params.get('type', 'N/A')}")
        
            # 处理情况3: 参数直接是监控项列表
            elif isinstance(processed_params, list):
                new_params = {"items": processed_params}
                for i, item in enumerate(processed_params):
                    if not isinstance(item, dict):
                        continue
                    
                    # 确保type参数存在且为整数
                    if "type" not in item or item["type"] is None:
                        logger.warning(f"预处理：第{i+1}个监控项缺少type参数，设置为默认值0")
                        new_params["items"][i]["type"] = 0
                    elif isinstance(item["type"], str):
                        # 如果是字符串，尝试转换为整数
                        try:
                            new_params["items"][i]["type"] = int(item["type"])
                            logger.warning(f"预处理：第{i+1}个监控项的type参数从字符串转换为整数")
                        except ValueError:
                            logger.warning(f"预处理：第{i+1}个监控项的type参数无法转换为整数，设置为默认值0")
                            new_params["items"][i]["type"] = 0
                
                return new_params
        
        # 处理trigger.create方法的参数
        elif method == "trigger.create":
            # 如果参数是单个触发器，转换为列表
            if isinstance(processed_params, dict):
                processed_params = [processed_params]
            
            # 确保参数是列表
            if isinstance(processed_params, list):
                for i, trigger in enumerate(processed_params):
                    if not isinstance(trigger, dict):
                        continue
                    
                    # 确保priority是整数
                    if "priority" in trigger:
                        if isinstance(trigger["priority"], str):
                            try:
                                processed_params[i]["priority"] = int(trigger["priority"])
                                logger.warning(f"预处理：第{i+1}个触发器的priority参数转换为整数")
                            except ValueError:
                                logger.warning(f"预处理：第{i+1}个触发器的priority参数无法转换为整数")
                    
                    # 确保recovery_mode是整数
                    if "recovery_mode" in trigger:
                        if isinstance(trigger["recovery_mode"], str):
                            try:
                                processed_params[i]["recovery_mode"] = int(trigger["recovery_mode"])
                                logger.warning(f"预处理：第{i+1}个触发器的recovery_mode参数转换为整数")
                            except ValueError:
                                logger.warning(f"预处理：第{i+1}个触发器的recovery_mode参数无法转换为整数")
        
        return processed_params

    def _call(self, method: str, params: Union[Dict[str, Any], List[Dict[str, Any]]]) -> Any:
        """
        调用Zabbix API方法
        
        Args:
            method (str): API方法名称
            params (Union[Dict[str, Any], List[Dict[str, Any]]]): API参数，可以是字典或字典列表
            
        Returns:
            Any: API返回结果
            
        Raises:
            ZabbixAPIException: API调用异常
        """
        # 记录API调用详情
        logger.debug(f"API调用: {method}")
        logger.debug(f"API原始参数: {json.dumps(params, ensure_ascii=False, indent=2)}")
        
        # 预处理参数
        processed_params = self._preprocess_api_params(method, params)
        
        # 构建请求数据
        data = {
            "jsonrpc": "2.0",
            "method": method,
            "params": processed_params,
            "id": self.id
        }
        
        # 如果已认证且不是登录请求，添加认证token
        if self.auth and method != "user.login":
            data["auth"] = self.auth
        
        # 递增请求ID
        self.id += 1
        
        # 记录最终参数
        logger.debug(f"API最终参数: {json.dumps(processed_params, ensure_ascii=False, indent=2)}")
        
        # 发送请求
        logger.debug(f"发送API请求: {method}")
        
        try:
            resp = requests.post(
                self.url,
                json=data,
                timeout=self.timeout,
                verify=self.verify
            )
            
            # 检查HTTP状态码
            if resp.status_code != 200:
                raise ZabbixAPIException(f"HTTP错误: {resp.status_code}")
            
            # 解析JSON响应
            try:
                result = resp.json()
            except Exception as e:
                raise ZabbixAPIException(f"无法解析JSON响应: {e}")
            
            # 检查API错误
            if "error" in result:
                err = result["error"]
                error_message = f"API错误 {err.get('code', 'unknown')}: {err.get('message', 'unknown')}, 数据: {err.get('data', 'no data')}"
                logger.error(error_message)
                raise ZabbixAPIException(error_message)
            
            # 记录API返回结果（摘要）
            if "result" in result:
                if isinstance(result["result"], list):
                    logger.debug(f"API返回: 列表包含 {len(result['result'])} 个项目")
                elif isinstance(result["result"], dict):
                    logger.debug(f"API返回: 字典包含 {len(result['result'])} 个键")
                else:
                    logger.debug(f"API返回: {result['result']}")
            
            # 返回结果
            return result.get("result")
        except Exception as e:
            logger.error(f"API调用异常: {str(e)}")
            raise
    
    def get_host_groups(self) -> List[Dict[str, Any]]:
        """
        获取所有主机群组
        
        Returns:
            List[Dict[str, Any]]: 主机群组列表
        """
        return self._call("hostgroup.get", {
            "output": ["groupid", "name"],
            "sortfield": "name"
        })
    
    def get_hosts_by_group_ids(self, group_ids: List[str], detail: bool = False) -> List[Dict[str, Any]]:
        """
        根据群组ID获取主机列表
        
        Args:
            group_ids (List[str]): 群组ID列表
            detail (bool, optional): 是否获取详细信息
            
        Returns:
            List[Dict[str, Any]]: 主机列表
        """
        params = {
            "groupids": group_ids,
            "sortfield": "name"
        }
        
        if detail:
            params["output"] = "extend"
            params["selectInterfaces"] = ["interfaceid", "ip", "dns", "useip", "port", "type", "main"]
            params["selectGroups"] = ["groupid", "name"]
        else:
            # 添加host字段(技术名称)，确保返回结果包含host和name两个字段
            params["output"] = ["hostid", "host", "name", "status"] 
        
        return self._call("host.get", params)
    
    def create_items(self, items: Union[Dict[str, Any], List[Dict[str, Any]]]) -> Dict[str, Any]:
        """
        批量创建监控项
        
        Args:
            items (Union[Dict[str, Any], List[Dict[str, Any]]]): 监控项配置列表或包含items列表的字典
            
        Returns:
            Dict[str, Any]: 创建结果
        """
        try:
            # 将参数标准化为包含items键的字典格式
            if isinstance(items, list):
                params = {"items": items}
            elif isinstance(items, dict) and "items" in items:
                params = items
            else:
                # 如果items是单个监控项，将其包装成列表
                params = {"items": [items]}
            
            # 确保params["items"]是列表
            if not isinstance(params["items"], list):
                params["items"] = [params["items"]]
            
            # 记录原始参数
            logger.debug(f"create_items方法原始参数: {json.dumps(params, ensure_ascii=False, indent=2)}")
            
            # 确保每个监控项都包含必要的type参数
            for i, item in enumerate(params["items"]):
                if not isinstance(item, dict):
                    logger.warning(f"第{i+1}个监控项不是字典类型，跳过处理")
                    continue
                    
                # 确保type参数存在且为整数
                if "type" not in item or item["type"] is None:
                    logger.warning(f"第{i+1}个监控项缺少type参数，设置为默认值0")
                    params["items"][i]["type"] = 0
                elif isinstance(item["type"], str):
                    # 如果是字符串，尝试转换为整数
                    try:
                        params["items"][i]["type"] = int(item["type"])
                        logger.warning(f"预处理：第{i+1}个监控项的type参数从字符串转换为整数")
                    except ValueError:
                        logger.warning(f"预处理：第{i+1}个监控项的type参数无法转换为整数，设置为默认值0")
                        params["items"][i]["type"] = 0
                
                # 记录每个监控项的关键信息
                logger.debug(f"监控项 {i+1}: name={item.get('name', 'N/A')}, type={item.get('type', 'N/A')}")
            
            # 再次记录修复后的参数
            logger.debug(f"create_items方法最终参数: {json.dumps(params, ensure_ascii=False, indent=2)}")
            
            # 调用API创建监控项
            result = self._call("item.create", params)
            logger.debug(f"监控项创建返回结果: {result}")
            return result
        except Exception as e:
            logger.error(f"创建监控项失败: {e}")
            raise
    
    def create_triggers(self, triggers: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        批量创建触发器
        
        Args:
            triggers (List[Dict[str, Any]]): 触发器配置列表
            
        Returns:
            List[Dict[str, Any]]: 创建结果
        """
        logger.debug(f"触发器创建参数: {triggers}")
        try:
            # 检查传入的参数是单个字典还是字典列表
            if isinstance(triggers, dict):
                # 如果是单个字典，转换为列表
                params = [triggers]
            else:
                # 如果已经是列表，直接使用
                params = triggers
                
            # 确保列表中的每个触发器参数都包含必要字段
            for i, trigger in enumerate(params):
                # 确保priority和recovery_mode是整数
                if 'priority' in trigger and isinstance(trigger['priority'], str):
                    try:
                        params[i]['priority'] = int(trigger['priority'])
                        logger.warning(f"预处理：第{i+1}个触发器的priority参数转换为整数")
                    except ValueError:
                        logger.warning(f"预处理：第{i+1}个触发器的priority参数无法转换为整数")
                if 'recovery_mode' in trigger and isinstance(trigger['recovery_mode'], str):
                    try:
                        params[i]['recovery_mode'] = int(trigger['recovery_mode'])
                        logger.warning(f"预处理：第{i+1}个触发器的recovery_mode参数转换为整数")
                    except ValueError:
                        logger.warning(f"预处理：第{i+1}个触发器的recovery_mode参数无法转换为整数")
                
            # 使用符合Zabbix API格式的调用
            result = self._call("trigger.create", params)
            logger.debug(f"触发器创建返回结果: {result}")
            return result
        except Exception as e:
            logger.error(f"创建触发器失败: {e}")
            # 重新抛出异常以便调用方处理
            raise
    
    def get_item_types(self) -> Dict[int, str]:
        """
        获取监控项类型列表
        
        Returns:
            Dict[int, str]: 监控项类型字典
        """
        # Zabbix API没有直接获取类型的方法，所以我们手动定义
        return {
            20: "SNMP Agent",
            11: "Database Monitor",
            12: "IPMI Agent",
            13: "SSH Agent",
            14: "TELNET Agent",
            15: "Calculated",
            16: "JMX Agent",
            17: "SNMP Trap",
            18: "Dependent Item",
            19: "HTTP Agent",
            0: "Zabbix Agent",
            2: "Zabbix Trapper",
            3: "Simple Check",
            5: "Zabbix Internal",
            7: "Zabbix Agent (active)",
            9: "Web Item",
            10: "External Check",
            21: "Script"
        }
    
    def get_value_types(self) -> Dict[int, str]:
        """
        获取数据类型列表
        
        Returns:
            Dict[int, str]: 数据类型字典
        """
        # Zabbix API没有直接获取类型的方法，所以我们手动定义
        return {
            0: "数值(浮点)",
            1: "字符",
            2: "日志",
            3: "数值(无符号)",
            4: "文本"
        }

    def get_host_interfaces(self, host_id: str) -> List[Dict[str, Any]]:
        """
        获取主机的接口信息
        
        Args:
            host_id (str): 主机ID
            
        Returns:
            List[Dict[str, Any]]: 接口信息列表
        """
        return self._call("hostinterface.get", {
            "output": ["interfaceid", "hostid", "type", "main", "useip", "ip", "dns", "port"],
            "hostids": host_id
        })

    def get_interface_types(self) -> Dict[int, str]:
        """
        获取接口类型列表
        
        Returns:
            Dict[int, str]: 接口类型字典
        """
        return {
            1: "Zabbix Agent",
            2: "SNMP",
            3: "IPMI",
            4: "JMX"
        } 